using System;
using System.Collections.Generic;
using System.Net;

namespace Socket_CS
{
    public class Params : IDisposable
    {
        private Dictionary<string, string> Named;
        private List<string> Unnamed;

        public static Params Instance;

        public Params()
        {
            Named = new Dictionary<string, string>();
            Unnamed = new List<string>();

            Instance = this;
            IDisposable x = this;
        }

        public void Dispose()
        {
            Instance = null;
        }

        public string PopLeftForUnnamed()
        {
            string str = Unnamed[0];
            Unnamed.RemoveAt(0);
            return str;
        }

        public void AddToNamed(string key, string value) { Named.Add(key, value); }
        public void AddToUnnamed(string value) { Unnamed.Add(value); }

        public void Parse(string[] args)
        {
            BaseArg argObject = new Arg_None();
            BaseArg nextArgObject;
            foreach (string arg in args)
            {
                nextArgObject = BaseArg.CreateFrom(arg);
                argObject.Add(nextArgObject);
                argObject = nextArgObject.Updated;
            }
            argObject.Add();
        }

        public string GetParamString(string key)
        {
            key = key.ToUpper();
            if (Named.ContainsKey(key)) return Named[key];
            else if (Unnamed.Count > 0) return PopLeftForUnnamed();
            else return null;
        }

        public IPAddress GetParamValue(string key, IPAddress defaultValue)
        {
            return GetParamValue<IPAddress>(key, defaultValue, Delegate_ParseIPAddress);
        }

        public int GetParamValue(string key, int defaultValue)
        {
            return GetParamValue<int>(key, defaultValue, Delegate_ParseInt);
        }

        public string GetParamValue(string key, string defaultValue)
        {
            return GetParamValue<string>(key, defaultValue, Delegate_ParseString);
        }

        public bool GetParamValue(string key, bool defaultValue)
        {
            return GetParamValue<bool>(key, defaultValue, Delegate_ParseBool);
        }

        public T GetParamValue<T>(string key, T defaultValue, Delegate_ParseValue<T> delegate_ParseValue)
        {
            string strParam = GetParamString(key);
            if (strParam == null) return defaultValue;

            try { return delegate_ParseValue(strParam); }
            catch (Exception e)
            {
                string error = String.Format("输入参数错误: 不能将 \"{0}\" 转换为{1}类型!", strParam, typeof(T));
                throw new Exception_StopApp(error, e);
            }
        }

        public delegate T Delegate_ParseValue<T>(string v);
        public static Delegate_ParseValue<IPAddress> Delegate_ParseIPAddress = new Delegate_ParseValue<IPAddress>(IPAddress.Parse);
        public static Delegate_ParseValue<int> Delegate_ParseInt = new Delegate_ParseValue<int>(int.Parse);
        public static Delegate_ParseValue<string> Delegate_ParseString = new Delegate_ParseValue<string>((string value) => { return value; });
        public static Delegate_ParseValue<bool> Delegate_ParseBool = new Delegate_ParseValue<bool>(bool.Parse);
    }

    public abstract class BaseArg
    {
        private string _StringArg;
        public string StringArg
        {
            get { return _StringArg; }
            set { _StringArg = value; }
        }

        public BaseArg Updated;
        public BaseArg(string arg) : this() { StringArg = arg; }
        public BaseArg() { Updated = this; }

        public static bool IsKeyString(string str) { return (str.Length > 0) && (str[0] == '-'); }

        public virtual bool IsKey { get { return false; } }
        public virtual bool IsValue { get { return false; } }
        public virtual bool isNone { get { return false; } }

        public static BaseArg CreateFrom(string arg)
        {
            return (IsKeyString(arg)) ? (BaseArg)(new Arg_Key(arg)) : (BaseArg)(new Arg_Value(arg));
        }

        public virtual void Add(Arg_Key key) { }
        public virtual void Add(Arg_Value value) { }
        public virtual void Add(Arg_None none) { }
        public virtual void Add() { }

        public void Add(BaseArg obj)
        {
            if (obj.IsKey) Add((Arg_Key)obj);
            else if (obj.IsValue) Add((Arg_Value)obj);
        }
    }

    public class Arg_Key : BaseArg
    {
        public Arg_Key(string arg) : base(arg.ToUpper()) { }
        public override bool IsKey { get { return true; } }
        public override void Add(Arg_Key key) { Add(); }
        public override void Add(Arg_Value value)
        {
            Params.Instance.AddToNamed(StringArg, value.StringArg);
            value.Updated = new Arg_None();
        }
        public override void Add(Arg_None none) { Add(); }
        public override void Add() { Add(new Arg_Value("")); }
    }

    public class Arg_Value : BaseArg
    {
        public Arg_Value(string arg) : base(arg) { }
        public override bool IsValue { get { return true; } }

        public override void Add(Arg_Key key) { Add(); }
        public override void Add(Arg_Value value) { Add(); }
        public override void Add(Arg_None none) { Add(); }
        public override void Add() { Params.Instance.AddToUnnamed(StringArg); }
    }

    public class Arg_None : BaseArg
    {
        public override bool isNone { get { return true; } }
    }
}
